"""
交互式视频生成主程序
支持用户在关键步骤进行干预、编辑和优化
已优化：并行图像生成，大幅提升速度
"""

import argparse
from networkx import selfloop_edges
import yaml
import sys
import os
import asyncio  # 新增：支持异步并行生成
from pathlib import Path
from typing import List, Optional, Tuple

# 🔥 新增: GUI 文件选择器
import tkinter as tk
from tkinter import filedialog

import platform
if platform.system() == 'Windows':
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
    try:
        import nest_asyncio
        nest_asyncio.apply()
    except ImportError:
        pass
# 启用Windows终端颜色支持
import platform
if platform.system() == 'Windows':
    try:
        import ctypes
        kernel32 = ctypes.windll.kernel32
        kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
    except:
        pass

from mm_story_agent.interactive_agent import InteractiveAgent
from mm_story_agent import MMStoryAgent
from mm_story_agent.optimized_image_generator import OptimizedImageGenerator  # 新增：优化生成器
from dotenv import load_dotenv
load_dotenv()

# ============================================================
# 🔥 新增: 文件选择工具函数
# ============================================================

def select_file_gui(title="选择要处理的文件"):
    """弹出GUI文件选择对话框"""
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    
    file_path = filedialog.askopenfilename(
        title=title,
        filetypes=[
            ("所有支持的文件", "*.pdf *.txt *.docx *.md *.epub"),
            ("PDF文件", "*.pdf"),
            ("文本文件", "*.txt *.md"),
            ("Word文档", "*.docx"),
            ("电子书", "*.epub"),
            ("所有文件", "*.*")
        ]
    )
    
    root.destroy()
    return file_path if file_path else None


def select_directory_gui(title="选择要处理的目录"):
    """弹出GUI目录选择对话框"""
    root = tk.Tk()
    root.withdraw()
    
    dir_path = filedialog.askdirectory(title=title)
    
    root.destroy()
    return dir_path if dir_path else None


# def resolve_input_path(config: dict, args):
#     """
#     解析输入路径(优先级:命令行 > 环境变量 > GUI选择 > 配置文件)
    
#     Args:
#         config: 配置字典
#         args: 命令行参数
    
#     Returns:
#         bool: 是否成功解析到有效路径
#     """
#     params = config['story_writer']['params']
#     input_type = params.get('input_type', 'topic')
    
#     # 如果不是 long_text 模式,跳过
#     if input_type != 'long_text':
#         print(f"当前模式: {input_type}, 无需选择文件")
#         return True
    
#     print("\n" + "="*60)
#     print("📁 输入文件解析")
#     print("="*60)
    
#     # 优先级1: 命令行参数
#     if hasattr(args, 'file') and args.file:
#         if os.path.exists(args.file):
#             params['file_path'] = args.file
#             params.pop('dir_path', None)
#             params.pop('long_text', None)
#             print(f"✅ 使用命令行指定的文件: {args.file}")
#             return True
#         else:
#             print(f"❌ 命令行指定的文件不存在: {args.file}")
#             return False
    
#     if hasattr(args, 'dir') and args.dir:
#         if os.path.exists(args.dir):
#             params['dir_path'] = args.dir
#             params.pop('file_path', None)
#             params.pop('long_text', None)
#             print(f"✅ 使用命令行指定的目录: {args.dir}")
#             return True
#         else:
#             print(f"❌ 命令行指定的目录不存在: {args.dir}")
#             return False
    
#     # 优先级2: 环境变量
#     env_file = os.environ.get('INPUT_FILE')
#     if env_file and os.path.exists(env_file):
#         params['file_path'] = env_file
#         params.pop('dir_path', None)
#         params.pop('long_text', None)
#         print(f"✅ 使用环境变量指定的文件: {env_file}")
#         return True
    
#     env_dir = os.environ.get('INPUT_DIR')
#     if env_dir and os.path.exists(env_dir):
#         params['dir_path'] = env_dir
#         params.pop('file_path', None)
#         params.pop('long_text', None)
#         print(f"✅ 使用环境变量指定的目录: {env_dir}")
#         return True
    
#     # 优先级3: 配置文件中的路径
#     file_path = params.get('file_path')
#     if file_path and str(file_path) != 'null' and os.path.exists(file_path):
#         print(f"✅ 使用配置文件中的路径: {file_path}")
#         return True
    
#     dir_path = params.get('dir_path')
#     if dir_path and str(dir_path) != 'null' and os.path.exists(dir_path):
#         print(f"✅ 使用配置文件中的目录: {dir_path}")
#         return True
    
#     # 优先级4: 检查是否有直接文本输入
#     long_text = params.get('long_text', '').strip()
#     if long_text and len(long_text) > 10:
#         print("✅ 使用配置文件中的直接文本输入")
#         params.pop('file_path', None)
#         params.pop('dir_path', None)
#         return True
    
#     # 优先级5: GUI选择(如果没有禁用)
#     if not (hasattr(args, 'no_gui') and args.no_gui):
#         print("\n⚠️  未指定有效的输入路径")
#         print("\n请选择输入方式:")
#         print("  1. 单个文件")
#         print("  2. 目录(批量处理)")
#         print("  3. 取消")
        
#         choice = input("\n请输入选项 [1-3] (默认:1): ").strip()
        
#         if choice == '2':
#             print("\n📁 请在弹出的对话框中选择目录...")
#             dir_path = select_directory_gui()
#             if dir_path:
#                 params['dir_path'] = dir_path
#                 params.pop('file_path', None)
#                 params.pop('long_text', None)
#                 print(f"✅ 已选择目录: {dir_path}")
#                 return True
#             else:
#                 print("❌ 未选择目录")
#                 return False
#         elif choice == '3':
#             print("❌ 用户取消")
#             return False
#         else:  # 默认选择文件
#             print("\n📄 请在弹出的对话框中选择文件...")
#             file_path = select_file_gui()
#             if file_path:
#                 params['file_path'] = file_path
#                 params.pop('dir_path', None)
#                 params.pop('long_text', None)
#                 print(f"✅ 已选择文件: {file_path}")
#                 return True
#             else:
#                 print("❌ 未选择文件")
#                 return False
    
#     # 所有方式都失败
#     print("\n" + "="*60)
#     print("❌ 错误: 未指定有效的输入文件或目录")
#     print("="*60)
#     print("\n可用的指定方式:")
#     print("  1. 命令行: --file <path> 或 --dir <path>")
#     print("  2. 环境变量: INPUT_FILE=<path> 或 INPUT_DIR=<path>")
#     print("  3. 修改配置文件中的 file_path 或 dir_path")
#     print("  4. 运行时选择 (不使用 --no-gui 参数)")
#     print("="*60)
    
#     return False

# def resolve_input_path(config: dict, args):
#     """
#     解析输入路径(优先级:命令行 > GUI选择 > 配置文件)
#     """
#     params = config['story_writer']['params']
#     input_type = params.get('input_type', 'topic')
    
#     # 如果不是 long_text 模式,跳过文件选择
#     if input_type != 'long_text':
#         print(f"当前模式: {input_type}, 无需选择文件")
#         return True
    
#     print("\n" + "="*60)
#     print("📁 输入文件解析")
#     print("="*60)
    
#     # 优先级1: 命令行参数（强制使用）
#     if hasattr(args, 'file') and args.file:
#         if os.path.exists(args.file):
#             params['file_path'] = args.file
#             params.pop('dir_path', None)
#             params.pop('long_text', None)  # 清除长文本
#             print(f"✅ 使用命令行指定的文件: {args.file}")
#             return True
#         else:
#             print(f"❌ 命令行指定的文件不存在: {args.file}")
#             return False
    
#     if hasattr(args, 'dir') and args.dir:
#         if os.path.exists(args.dir):
#             params['dir_path'] = args.dir
#             params.pop('file_path', None)
#             params.pop('long_text', None)  # 清除长文本
#             print(f"✅ 使用命令行指定的目录: {args.dir}")
#             return True
#         else:
#             print(f"❌ 命令行指定的目录不存在: {args.dir}")
#             return False
    
#     # 优先级2: GUI选择（如果没有禁用GUI）
#     if not (hasattr(args, 'no_gui') and args.no_gui):
#         print("\n📁 请选择输入方式:")
#         print("  1. 选择文件")
#         print("  2. 选择目录")
#         print("  3. 使用配置中的长文本")
#         print("  4. 取消")
        
#         choice = input("\n请输入选项 [1-4] (默认:1): ").strip()
        
#         if choice == '1':
#             print("\n📄 请在弹出的对话框中选择文件...")
#             file_path = select_file_gui()
#             if file_path:
#                 params['file_path'] = file_path
#                 params.pop('dir_path', None)
#                 params.pop('long_text', None)
#                 print(f"✅ 已选择文件: {file_path}")
#                 return True
        
#         elif choice == '2':
#             print("\n📁 请在弹出的对话框中选择目录...")
#             dir_path = select_directory_gui()
#             if dir_path:
#                 params['dir_path'] = dir_path
#                 params.pop('file_path', None)
#                 params.pop('long_text', None)
#                 print(f"✅ 已选择目录: {dir_path}")
#                 return True
        
#         elif choice == '3':
#             # 用户明确选择使用长文本
#             long_text = params.get('long_text', '')
#             if long_text and len(long_text) > 10:
#                 print("✅ 使用配置文件中的直接文本输入")
#                 return True
#             else:
#                 print("❌ 配置文件中没有有效的长文本")
#                 return False
        
#         else:  # 取消或无效选择
#             print("❌ 用户取消")
#             return False
    
#     # 优先级3: 检查配置文件中的文件路径（只有在没有GUI时才检查）
#     file_path = params.get('file_path')
#     if file_path and str(file_path) != 'null' and os.path.exists(file_path):
#         print(f"✅ 使用配置文件中的路径: {file_path}")
#         return True
    
#     dir_path = params.get('dir_path')
#     if dir_path and str(dir_path) != 'null' and os.path.exists(dir_path):
#         print(f"✅ 使用配置文件中的目录: {dir_path}")
#         return True
    
#     # 优先级4: 最后才检查长文本（只有在没有其他选择时）
#     long_text = params.get('long_text', '').strip()
#     if long_text and len(long_text) > 10:
#         print("⚠️  使用配置文件中的直接文本输入（没有找到文件）")
#         return True
    
#     # 所有方式都失败
#     print("\n❌ 错误: 未指定有效的输入文件或目录")
#     return False

def resolve_input_path(config: dict, args):
    """
    解析输入路径
    """
    if not config:
        print("❌ 错误：配置为空")
        return False
    
    if 'story_writer' not in config:
        print("❌ 错误：配置中缺少 story_writer 部分")
        return False
    
    story_writer = config.get('story_writer', {})
    params = story_writer.get('params', {})  # 使用安全的 .get() 方法
    
    if not params:
        print("❌ 错误：story_writer.params 为空")
        return False
    
    input_type = params.get('input_type', 'topic')
    
    print("\n" + "="*60)
    print("📁 输入文件解析")
    print("="*60)

    params = config['story_writer']['params']
    input_type = params.get('input_type', 'topic')
    
    print("\n" + "="*60)
    print("📁 输入文件解析")
    print("="*60)
    
    # 长文本模式：提供两种选择
    if input_type == 'long_text':
        print("📝 长文本模式：请选择输入方式")
        print("  1. 📄 选择文件（处理长文档）")
        print("  2. ⌨️  直接输入（与AI交互）")
        print("  3. 📋 使用配置中的预设文本")
        print("  4. ❌ 取消")
        
        choice = input("\n请输入选项 [1-4] (默认:2): ").strip() or "2"
        
        if choice == "1":
            # 文件选择模式
            print("\n📄 请在弹出的对话框中选择文件...")
            file_path = select_file_gui()
            if file_path:
                params['file_path'] = file_path
                params.pop('long_text', None)  # 清除直接文本
                print(f"✅ 已选择文件: {file_path}")
                return True
            else:
                print("❌ 未选择文件")
                return False
                
        elif choice == "2":
            # 直接输入模式（与LLM交互）
            print("\n⌨️  直接输入模式（支持与AI多轮对话）")
            user_input = input("请输入故事主题或内容: ").strip()
            if user_input:
                params['long_text'] = user_input  # 更新为用户的输入
                params.pop('file_path', None)     # 清除文件路径
                print(f"✅ 已输入内容: {user_input[:50]}...")
                return True
            else:
                print("❌ 输入内容为空")
                return False
                
        elif choice == "3":
            # 使用配置中的预设文本
            long_text = params.get('long_text', '')
            if long_text and len(long_text) > 10:
                print("✅ 使用配置文件中的预设文本")
                print(f"文本长度: {len(long_text)} 字符")
                return True
            else:
                print("❌ 配置中没有有效的预设文本")
                return False
                
        else:
            print("❌ 用户取消")
            return False
    
    # 主题模式：直接键盘输入
    elif input_type == 'topic':
        print("✅ 主题模式：将在运行时通过键盘输入故事主题")
        return True
    
    return True

class InteractiveVideoGenerator:
    """交互式视频生成器"""
    
    def __init__(self, config: dict):
        self.config = config
        self.interactive = InteractiveAgent()
        self.agent = MMStoryAgent()
        
        # 提取关键配置
        self.story_dir = Path(config.get('story_dir', 'generated_stories/example'))
        self.story_dir.mkdir(parents=True, exist_ok=True)
 
    # def run(self):
    #     """执行交互式生成流程"""
    #     self.interactive.print_header("🎬 交互式视频生成系统")
        
    #     try:
    #         # 阶段1-4: 故事生成和编辑（保持不变）
    #         #pages = self.stage_1_generate_story()
    #         pages = self.enhanced_stage_1()
    #         if not pages:
    #             return
    #         pages = self.stage_2_edit_story(pages)
    #         prompts = self.stage_3_generate_prompts(pages)
    #         prompts = self.stage_4_edit_prompts(prompts, pages)
            
    #         # 🔥 新增：阶段4.5 - 生成数据可视化
    #         viz_paths = self.stage_4_5_generate_visualizations(pages)
            
    #         # 阶段5-8: 图像生成和视频合成（保持不变）
    #         image_paths = self.stage_5_generate_images(prompts, pages)
    #         image_paths = self.stage_6_regenerate_images(image_paths, prompts, pages)
            
    #         # 🔥 合并可视化图表到图像列表
    #         if viz_paths:
    #             image_paths = self.merge_visualizations(image_paths, viz_paths, pages)
            
    #         if not self.stage_7_final_confirmation(pages, image_paths):
    #             self.interactive.print_warning("用户取消了视频合成")
    #             return
            
    #         self.stage_8_compose_video(pages, image_paths)
    #         self.interactive.print_success("🎉 视频生成完成！")
            
    #     except KeyboardInterrupt:
    #         self.interactive.print_warning("\n⚠️  用户中断了操作")
    #         sys.exit(0)
    #     except Exception as e:
    #         self.interactive.print_error(f"发生错误: {e}")
    #         import traceback
    #         traceback.print_exc()
    #         sys.exit(1)
    def run(self):
        """执行交互式生成流程"""
        self.interactive.print_header("🎬 交互式视频生成系统")
        
        try:
            # 阶段1-2: 故事生成和编辑（先获取页面数量）
            pages = self.enhanced_stage_1()
            if not pages:
                return
            pages = self.stage_2_edit_story(pages)
            
            # 🔥 新增: 检查是否已有提示词和音频文件（在获取pages之后）
            existing_prompts, existing_speech_files = self._check_existing_assets(pages)
            skip_prompts = False
            skip_speech = False
            
            if existing_prompts or existing_speech_files:
                self.interactive.print_header("📋 检测到已有资源")
                
                if existing_prompts and len(existing_prompts) >= len(pages):
                    self.interactive.print_info(f"发现已有提示词: {len(existing_prompts)} 个（需要 {len(pages)} 个）")
                    skip_prompts = self.interactive.get_yes_no("是否跳过提示词生成步骤？", True)
                elif existing_prompts:
                    self.interactive.print_warning(
                        f"发现已有提示词: {len(existing_prompts)} 个，但需要 {len(pages)} 个，将重新生成"
                    )
                
                if existing_speech_files and len(existing_speech_files) >= len(pages):
                    self.interactive.print_info(f"发现已有音频文件: {len(existing_speech_files)} 个（需要 {len(pages)} 个）")
                    skip_speech = self.interactive.get_yes_no("是否跳过语音生成步骤？", True)
                elif existing_speech_files:
                    self.interactive.print_warning(
                        f"发现已有音频文件: {len(existing_speech_files)} 个，但需要 {len(pages)} 个，将重新生成"
                    )
            
            # 根据用户选择决定是否生成提示词
            if skip_prompts and existing_prompts and len(existing_prompts) >= len(pages):
                # 只使用前 len(pages) 个提示词
                prompts = existing_prompts[:len(pages)]
                self.interactive.print_success(f"使用已有提示词: {len(prompts)} 个")
            else:
                prompts = self.stage_3_generate_prompts(pages)
                prompts = self.stage_4_edit_prompts(prompts, pages)
            
            viz_paths = self.stage_4_5_generate_visualizations(pages)
            
            # 🔥 新增: 阶段3.5 - 生成讲述性文本（用于语音和字幕）
            narration_texts = self.stage_3_5_generate_narration_text(pages)
            
            # 🔥 根据用户选择决定是否生成语音
            if skip_speech and existing_speech_files and len(existing_speech_files) >= len(pages):
                speech_generated = True
                self.interactive.print_success(f"使用已有音频文件: {len(existing_speech_files)} 个")
            else:
                # 🔥 新增: 阶段5.5 - 语音生成（使用讲述性文本）
                speech_generated = self.stage_5_5_generate_speech(narration_texts)
            
            if not speech_generated:
                self.interactive.print_warning(
                    "未生成语音文件，视频将使用默认时长（每页5秒）"
                )
                if not self.interactive.get_yes_no("是否继续？", True):
                    return
            
            # 🔥 新增: 让用户选择生成模式
            self.interactive.print_header("🎯 选择视频生成模式")
            mode_choice = self.interactive.get_choice(
                "请选择视频生成方式：",
                ["🖼️  图片拼接成视频（传统模式）", "🎬 视频片段拼接成视频（Runway API）"],
                0
            )
            
            video_paths = None
            image_paths = None
            
            if mode_choice == 0:
                # 模式1: 图片拼接成视频（传统模式）
                self.interactive.print_info("选择模式：图片拼接成视频")
                image_paths = self.stage_5_generate_images(prompts, pages)
                image_paths = self.stage_6_regenerate_images(image_paths, prompts, pages)
                
            else:
                # 模式2: 视频片段拼接成视频（Runway API）
                self.interactive.print_info("选择模式：视频片段拼接成视频")
                video_paths, fallback_image_paths = self.stage_5_6_generate_videos(
                    prompts, pages, image_paths=None
                )
                
                # 对于视频生成失败的页面，使用回退的图片
                image_paths = fallback_image_paths
            
            # 合并可视化图表
            if viz_paths:
                image_paths = self.merge_visualizations(image_paths, viz_paths, pages)
            
            # 最终确认
            if not self.stage_7_final_confirmation(pages, image_paths, video_paths):
                self.interactive.print_warning("用户取消了视频合成")
                return
            
            # 合成视频（使用讲述性文本作为字幕）
            self.stage_8_compose_video(narration_texts, image_paths, video_paths)
            
            # 🔥 新增：阶段9 - 生成交互式HTML
            self.stage_9_generate_interactive_html(pages, viz_paths)

            self.interactive.print_success("🎉 所有内容生成完成！")
            
        except KeyboardInterrupt:
            self.interactive.print_warning("\n⚠️  用户中断了操作")
            sys.exit(0)
        except Exception as e:
            self.interactive.print_error(f"发生错误: {e}")
            import traceback
            traceback.print_exc()
            sys.exit(1)



    def _display_final_summary(self, analysis: dict):
        """显示最终分析摘要"""
        self.interactive.print_header("📊 内容分析摘要")
        
        print("✅ 基于以下分析结果生成视频:")
        
        themes = analysis.get('themes', [])
        if themes:
            print(f"\n🎯 核心主题: {', '.join(themes[:3])}")
        
        sentiment = analysis.get('sentiment', {})
        if sentiment:
            print(f"💭 情感基调: {sentiment.get('overall', 'N/A')}")
        
        print(f"\n详细分析报告已保存至: {self.story_dir}/content_analysis.json")
        print("="*60)
        
    def stage_4_5_generate_visualizations(self, pages: list) -> list:
        """阶段4.5: 生成数据可视化"""
        self.interactive.print_header("📊 阶段4.5: 数据可视化生成")
        
        # 检查是否启用
        viz_config = self.config.get('data_visualization', {})
        if not viz_config.get('enabled', False):
            self.interactive.print_info("数据可视化未启用，跳过此阶段")
            return []
        
        # 询问用户
        if not self.interactive.get_yes_no("是否生成数据可视化图表？", True):
            self.interactive.print_warning("跳过数据可视化")
            return []
        
        # 导入可视化模块
        from mm_story_agent.modality_agents.data_visualization_agent import DataVisualizationAgent
        
        # 创建可视化代理
        viz_agent = DataVisualizationAgent(
            output_dir=self.story_dir / "visualizations",
            style=viz_config.get('style', 'colorful')
        )
        
        # 生成可视化
        self.interactive.print_info("正在分析故事内容并生成图表...")
        viz_paths = viz_agent.generate_all_visualizations(
            pages,
            animated=viz_config.get('animated', True)
        )
        
        # 保存进度
        self.save_progress({
            'pages': pages,
            'visualizations': viz_paths
        })
        
        return viz_paths

    def merge_visualizations(self, image_paths: list, viz_paths: list, 
                            pages: list) -> list:
        """合并可视化图表到图像列表"""
        self.interactive.print_header("🔀 合并可视化图表")
        
        if not viz_paths:
            return image_paths
        
        # 策略1: 在特定位置插入
        merged = image_paths.copy()
        
        # 在每3页后插入一个可视化
        insert_interval = max(1, len(pages) // len(viz_paths))
        
        for i, viz_path in enumerate(viz_paths):
            insert_pos = min((i + 1) * insert_interval, len(merged))
            merged.insert(insert_pos, viz_path)
            self.interactive.print_success(f"✅ 在位置 {insert_pos} 插入图表")
        
        self.interactive.print_info(f"合并后共 {len(merged)} 张图片（含 {len(viz_paths)} 张图表）")
        
        return merged
    


# 在 InteractiveVideoGenerator 类中添加以下方法

        def stage_0_5_analyze_content(self, params: dict) -> dict:
            """
            🔥 新增：阶段0.5 - 内容分析（在故事生成前）
            
            对输入内容进行深度分析：
            - 主题识别
            - 情感分析
            - 要点提炼
            - 叙事结构建议
            """
            self.interactive.print_header("📊 阶段0.5: 内容深度分析")
            
            # 检查是否启用内容分析
            analysis_config = self.config.get('content_analysis', {})
            if not analysis_config.get('enabled', False):
                self.interactive.print_info("内容分析未启用，跳过此阶段")
                return {}
            
            # 询问用户是否进行分析
            if not self.interactive.get_yes_no("是否对输入内容进行深度分析？", True):
                self.interactive.print_warning("跳过内容分析")
                return {}
            
            self.interactive.print_info("正在进行内容分析...")
            
            # 准备分析参数
            from mm_story_agent.base import init_tool_instance
            analyzer = init_tool_instance(analysis_config)
            
            analysis_params = analysis_config.get('params', {}).copy()
            
            # 根据输入类型准备文本
            story_params = self.config['story_writer']['params']
            if story_params.get('file_path'):
                analysis_params['file_path'] = story_params['file_path']
            elif story_params.get('dir_path'):
                analysis_params['dir_path'] = story_params['dir_path']
            elif story_params.get('long_text'):
                analysis_params['text'] = story_params['long_text']
            else:
                self.interactive.print_warning("未找到可分析的输入内容")
                return {}
            
            # 设置输出路径
            if analysis_params.get('save_to_file', False):
                output_filename = analysis_params.get('output_filename', 'content_analysis.json')
                analysis_params['output_path'] = self.story_dir / output_filename
            
            # 执行分析
            try:
                analysis_result = analyzer.call(analysis_params)
                
                # 询问是否根据分析结果调整生成策略
                if self.interactive.get_yes_no("\n是否根据分析结果调整视频生成策略？", True):
                    self._apply_analysis_to_generation(analysis_result, params)
                
                # 保存分析结果到进度
                self.save_progress({
                    'content_analysis': analysis_result
                })
                
                return analysis_result
                
            except Exception as e:
                self.interactive.print_error(f"内容分析失败: {e}")
                import traceback
                traceback.print_exc()
                return {}

    def _apply_analysis_to_generation(self, analysis: dict, params: dict):
        """
        根据分析结果调整生成策略
        """
        self.interactive.print_section("🎯 应用分析结果")
        
        # 1. 根据情感基调调整风格
        sentiment = analysis.get('sentiment', {}).get('overall', 'neutral')
        narrative_style = analysis.get('narrative_structure', {}).get('style', '')
        
        current_style = params.get('style', 'narrative')
        print(f"\n当前视频风格: {current_style}")
        print(f"建议风格: {narrative_style}")
        print(f"情感基调: {sentiment}")
        
        if self.interactive.get_yes_no("是否采用建议的叙事风格？", False):
            # 映射分析建议到已有风格选项
            style_map = {
                "纪录片": "documentary",
                "documentary": "documentary",
                "教程": "tutorial",
                "tutorial": "tutorial",
                "故事": "narrative",
                "narrative": "narrative"
            }
            for key, value in style_map.items():
                if key in narrative_style.lower():
                    params['style'] = value
                    print(f"✅ 已调整风格为: {value}")
                    break
        
        # 2. 根据段落建议调整目标页数
        segments = analysis.get('narrative_structure', {}).get('segments', [])
        if segments:
            suggested_pages = len(segments)
            current_pages = params.get('target_pages', 8)
            
            print(f"\n当前目标页数: {current_pages}")
            print(f"建议段落数: {suggested_pages}")
            
            if self.interactive.get_yes_no(f"是否调整为建议的 {suggested_pages} 页？", False):
                params['target_pages'] = suggested_pages
                print(f"✅ 已调整目标页数为: {suggested_pages}")
        
        # 3. 显示关键要点（可用于后续提示词优化）
        key_points = analysis.get('key_points', [])
        if key_points:
            print(f"\n📌 关键要点（将用于优化图像生成）:")
            for i, point in enumerate(key_points[:3], 1):
                print(f"  {i}. {point}")














    def stage_1_generate_story(self) -> list:
        """阶段1: 生成故事内容"""
        self.interactive.print_header("📖 阶段1: 生成故事内容")
        
        # 检查是否有已保存的会话
        session = self.interactive.load_session(self.story_dir)
        if session and 'pages' in session:
            if self.interactive.get_yes_no("检测到之前的会话，是否继续？", True):
                self.interactive.print_success(f"已加载 {len(session['pages'])} 页故事")
                return session['pages']
        
        # 显示配置信息
        story_config = self.config.get('story_writer', {})
        params = story_config.get('params', {})
        input_type = params.get('input_type', 'topic')
        
        print(f"\n当前配置:")
        print(f"  模式: {input_type}")
        if input_type == 'long_text':
            long_text = params.get('long_text', '')
            print(f"  文本预览: {long_text[:100]}...")
        else:
            print(f"  主题: {params.get('story_topic', 'N/A')}")
        
        target_pages = params.get('target_pages', 8)
        print(f"  目标页数: {target_pages}")
        
        # 确认是否开始生成
        if not self.interactive.get_yes_no("\n开始生成故事？", True):
            return []
        
        # 调用故事生成
        self.interactive.print_info("正在生成故事...")
        
        # 这里使用原有的story_writer
        from mm_story_agent.base import init_tool_instance
        story_writer = init_tool_instance(story_config)
        pages = story_writer.call(params)
        
        self.interactive.print_success(f"故事生成完成！共 {len(pages)} 页")
        
        # 保存中间结果
        self.save_progress({'pages': pages})
        
        return pages
    
    def stage_2_edit_story(self, pages: list) -> list:
        """阶段2: 编辑故事内容"""
        self.interactive.print_header("✏️  阶段2: 编辑故事内容")
        
        # 使用交互式编辑
        edited_pages = self.interactive.review_and_edit_pages(pages)
        
        # 保存编辑结果
        self.save_progress({'pages': edited_pages})
        
        return edited_pages
    
    def stage_3_generate_prompts(self, pages: list) -> list:
        """阶段3: 生成图像提示词"""
        self.interactive.print_header("🎨 阶段3: 生成图像提示词")
        
        self.interactive.print_info("正在为每个页面生成图像提示词...")
        
        # 使用image_agent的提示词生成功能
        from mm_story_agent.base import init_tool_instance
        image_config = self.config.get('image_generation', {})
        
        # 创建临时的image agent来生成提示词
        image_agent = init_tool_instance(image_config)
        
        # 提取角色
        role_dict = image_agent.extract_role_from_story(pages)
        self.interactive.print_success(f"识别到 {len(role_dict)} 个角色")
        
        # 生成提示词
        prompts = image_agent.generate_image_prompt_from_story(pages)
        
        # 添加角色描述
        prompts_with_roles = []
        for prompt in prompts:
            for role, role_desc in role_dict.items():
                if role in prompt:
                    prompt = prompt.replace(role, role_desc)
            prompts_with_roles.append(prompt)
        
        self.interactive.print_success(f"生成了 {len(prompts_with_roles)} 个提示词")
        
        # 保存
        self.save_progress({
            'pages': pages,
            'prompts': prompts_with_roles,
            'roles': role_dict
        })
        
        return prompts_with_roles
    
    def stage_4_edit_prompts(self, prompts: list, pages: list) -> list:
        """阶段4: 优化提示词"""
        self.interactive.print_header("✨ 阶段4: 优化提示词")
        
        # 交互式编辑提示词
        edited_prompts = self.interactive.review_and_edit_prompts(prompts, pages)
        
        # 保存
        self.save_progress({
            'pages': pages,
            'prompts': edited_prompts
        })
        
        return edited_prompts
    
    def stage_5_generate_images(self, prompts: list, pages: list) -> list:
        """阶段5: 生成图像（优化版 - 并行生成）"""
        self.interactive.print_header("🖼️ 阶段5: 生成图像（并行模式）")
        
        self.interactive.print_info(f"准备并行生成 {len(prompts)} 张图片...")
        
        if not self.interactive.get_yes_no("开始生成图像？（使用并行加速）", True):
            self.interactive.print_warning("跳过图像生成")
            return []
        
        # 获取配置
        image_config = self.config.get('image_generation', {})
        params = image_config.get('params', {})
        
        # 优先从环境变量读取 API key
        import os
        api_key = params.get('api_key') or os.getenv('DASHSCOPE_API_KEY')
        
        if not api_key:
            self.interactive.print_error("未配置 api_key")
            self.interactive.print_warning("请在配置文件中设置 api_key 或运行 setup_env.ps1")
            return []
        
        # 准备路径
        image_dir = self.story_dir / "image"
        cache_dir = self.story_dir / ".cache"
        
        # 优化提示词
        from mm_story_agent.base import init_tool_instance
        image_agent = init_tool_instance(image_config)
        style_name = params.get('style_name', 'Storybook')
        
        optimized_prompts = []
        for prompt in prompts:
            opt_prompt = image_agent.optimize_prompt(prompt, style_name)
            optimized_prompts.append(opt_prompt)
        
        # 创建优化生成器
        generator = OptimizedImageGenerator(
            api_key=api_key,
            max_concurrent=params.get('max_concurrent', 4),
            request_interval=params.get('request_interval', 1.0),
            cache_dir=cache_dir if params.get('enable_cache', True) else None,
            max_retries=params.get('max_retries', 3)
        )
        try:
            # 检查是否有运行中的事件循环
            loop = asyncio.get_event_loop()
            if loop.is_running():
                # 如果有运行中的循环，创建新的循环
                import nest_asyncio
                nest_asyncio.apply()
        except RuntimeError:
            pass
        # 并行生成
        self.interactive.print_info("⚡ 使用并行模式生成中...")
        images = asyncio.run(
            generator.generate_batch(optimized_prompts, image_dir)
        )
        
        # 保存进度
        self.save_progress({
            'pages': pages,
            'prompts': prompts,
            'images': images
        })
        
        # 统计成功率
        success_count = sum(1 for img in images if img is not None)
        self.interactive.print_success(
            f"图像生成完成！成功 {success_count}/{len(images)} 张"
        )
        
        return images
    
    def stage_5_5_generate_speech(self, narration_texts: list) -> bool:
    
        self.interactive.print_header("🎙️ 阶段5.5: 生成语音")
        
        # 检查配置
        speech_config = self.config.get('speech_generation')
        if not speech_config:
            self.interactive.print_warning("未配置语音生成，将使用静音")
            return False
        
        # 检查语音目录
        speech_dir = self.story_dir / "speech"
        speech_dir.mkdir(parents=True, exist_ok=True)
        
        # 检查是否已有语音文件
        existing_speech = list(speech_dir.glob("p*.wav"))
        if existing_speech and len(existing_speech) >= len(narration_texts):
            if self.interactive.get_yes_no(
                f"检测到 {len(existing_speech)} 个已存在的语音文件，是否跳过生成？", 
                True
            ):
                self.interactive.print_success("使用已存在的语音文件")
                return True
        
        # 询问是否生成语音
        if not self.interactive.get_yes_no("开始生成语音？", True):
            self.interactive.print_warning("跳过语音生成，视频将使用默认时长")
            return False
        
        # 显示配置信息
        params = speech_config.get('params', {})
        cfg = speech_config.get('cfg', {})
        tool_name = speech_config.get('tool', '')
        
        print(f"\n当前配置:")
        print(f"  工具: {tool_name}")
        print(f"  采样率: {cfg.get('sample_rate', 16000)} Hz")
        print(f"  音色: {params.get('voice', 'longyuan')}")
        
        # 根据工具类型检查必要的密钥
        if tool_name == 'cosyvoice_tts':
            # DashScope API 只需要 api_key
            if not params.get('api_key') and not os.getenv('DASHSCOPE_API_KEY'):
                self.interactive.print_error("缺少必要的密钥: api_key")
                self.interactive.print_warning("请在配置文件的 speech_generation.params 中设置 api_key (DashScope API Key)")
                self.interactive.print_warning("或设置环境变量 DASHSCOPE_API_KEY")
                return False
        elif tool_name == 'nls_t2s':
            # 阿里云 NLS 需要 access_key_id, access_key_secret, app_key
            required_keys = ['access_key_id', 'access_key_secret', 'app_key']
            missing_keys = [k for k in required_keys if not params.get(k)]
            if missing_keys:
                self.interactive.print_error(f"缺少必要的密钥: {', '.join(missing_keys)}")
                self.interactive.print_warning("请在配置文件中设置或运行 setup_env.ps1")
                return False
        else:
            # 未知工具，尝试检查 api_key
            if not params.get('api_key') and not os.getenv('DASHSCOPE_API_KEY'):
                self.interactive.print_warning(f"未知工具 {tool_name}，建议检查配置")
        
        # 导入并创建语音生成代理
        try:
            from mm_story_agent.base import init_tool_instance
            speech_agent = init_tool_instance(speech_config)
            self.interactive.print_success("语音生成器初始化成功")
        except Exception as e:
            self.interactive.print_error(f"初始化语音生成器失败: {e}")
            return False
        
        # 准备参数（批量生成所有页面，使用讲述性文本）
        speech_params = params.copy()
        speech_params['pages'] = narration_texts  # 使用讲述性文本而不是原始页面
        speech_params['save_path'] = speech_dir
        
        # 🔥 性能优化：尝试使用并行生成（仅 cosyvoice_tts）
        if tool_name == 'cosyvoice_tts':
            api_key = params.get('api_key') or os.getenv('DASHSCOPE_API_KEY')
            if api_key:
                try:
                    from mm_story_agent.performance_optimizer import ParallelSpeechGenerator
                    
                    generator = ParallelSpeechGenerator(
                        api_key=api_key,
                        max_concurrent=params.get('max_concurrent', 3),
                        request_interval=params.get('request_interval', 0.3)
                    )
                    
                    voice = params.get('voice', 'longxiaochun_v2')
                    max_chars = params.get('max_chars_per_request', 400)
                    max_retries = params.get('max_retries', 3)
                    
                    self.interactive.print_info(f"⚡ 使用并行模式生成语音（{generator.max_concurrent}个并发）...")
                    
                    # 异步生成（使用讲述性文本）
                    results = asyncio.run(
                        generator.generate_batch(
                            narration_texts, speech_dir, voice, max_chars, max_retries
                        )
                    )
                    
                    success_count = sum(1 for v in results.values() if v)
                    total_count = len(narration_texts)
                    
                    self.interactive.print_success(
                        f"语音生成完成: {success_count}/{total_count} 个页面"
                    )
                    
                    # 保存进度
                    generated_files = list(speech_dir.glob("p*.wav")) + list(speech_dir.glob("p*_*.wav"))
                    self.save_progress({
                        'narration_texts': narration_texts,
                        'speech_files': [str(f) for f in generated_files]
                    })
                    
                    return success_count > 0
                    
                except Exception as e:
                    self.interactive.print_warning(f"并行生成失败，回退到串行模式: {e}")
                    import traceback
                    traceback.print_exc()
                    # 继续使用原有方法
        
        # 生成语音（批量调用 - 串行模式，使用讲述性文本）
        self.interactive.print_info(f"正在为 {len(narration_texts)} 个页面生成语音...")
        
        try:
            # 直接调用批量生成方法
            speech_agent.call(speech_params)
            
            # 检查生成结果
            generated_files = list(speech_dir.glob("p*.wav"))
            # 也检查分块文件（p1_001.wav, p1_002.wav 等）
            chunked_files = list(speech_dir.glob("p*_*.wav"))
            all_files = generated_files + chunked_files
            success_count = len([f for f in all_files if f.exists()])
            
            self.interactive.print_success(
                f"语音生成完成: {success_count} 个文件（包含分块文件）"
            )
            
            # 保存进度
            self.save_progress({
                'narration_texts': narration_texts,
                'speech_files': [str(f) for f in all_files]
            })
            
            return success_count > 0
            
        except Exception as e:
            self.interactive.print_error(f"语音生成失败: {e}")
            import traceback
            traceback.print_exc()
            return False


    def stage_5_5_generate_speech_batch(self, pages: list) -> bool:
        """
        阶段5.5: 批量生成语音（使用原有的并行方法）
        这个版本调用 MMStoryAgent 的方法，支持并行生成
        """
        self.interactive.print_header("🎙️ 阶段5.5: 批量生成语音")
        
        # 检查配置
        speech_config = self.config.get('speech_generation')
        if not speech_config:
            self.interactive.print_warning("未配置语音生成")
            return False
        
        # 询问是否生成
        if not self.interactive.get_yes_no("开始批量生成语音？", True):
            self.interactive.print_warning("跳过语音生成")
            return False
        
        # 检查已存在的文件
        speech_dir = self.story_dir / "speech"
        speech_dir.mkdir(parents=True, exist_ok=True)
        existing = list(speech_dir.glob("p*.wav"))
        
        if existing:
            self.interactive.print_info(f"已存在 {len(existing)} 个语音文件")
            if self.interactive.get_yes_no("是否跳过已生成的？", True):
                if len(existing) >= len(pages):
                    return True
        
        try:
            # 使用 MMStoryAgent 的方法
            self.interactive.print_info("正在调用语音生成服务...")
            
            # 准备临时配置
            temp_config = {
                'speech_generation': speech_config,
                'story_dir': str(self.story_dir)
            }
            
            # 调用 MMStoryAgent 的语音生成
            from mm_story_agent.base import init_tool_instance
            speech_agent = init_tool_instance(speech_config)
            
            params = speech_config.get('params', {}).copy()
            params['pages'] = pages
            params['save_path'] = speech_dir
            
            # 执行生成
            result = speech_agent.call(params)
            
            # 检查结果
            generated = list(speech_dir.glob("p*.wav"))
            success = len(generated) > 0
            
            if success:
                self.interactive.print_success(
                    f"✅ 语音生成完成: {len(generated)}/{len(pages)} 个文件"
                )
            else:
                self.interactive.print_warning("⚠️ 没有生成语音文件")
            
            return success
            
        except Exception as e:
            self.interactive.print_error(f"语音生成失败: {e}")
            import traceback
            traceback.print_exc()
            return False


    def merge_visualizations(self, image_paths: list, viz_paths: list, 
                            pages: list) -> list:
        """合并可视化图表到图像列表"""
        self.interactive.print_header("🔀 合并可视化图表")
        
        if not viz_paths:
            return image_paths
        
        # 策略1: 在特定位置插入
        merged = image_paths.copy()
        
        # 在每3页后插入一个可视化
        insert_interval = max(1, len(pages) // len(viz_paths))
        
        for i, viz_path in enumerate(viz_paths):
            insert_pos = min((i + 1) * insert_interval, len(merged))
            merged.insert(insert_pos, viz_path)
            self.interactive.print_success(f"✅ 在位置 {insert_pos} 插入图表")
        
        self.interactive.print_info(f"合并后共 {len(merged)} 张图片（含 {len(viz_paths)} 张图表）")
        
        return merged
    def stage_1_generate_story(self) -> list:
        """阶段1: 生成故事内容"""
        self.interactive.print_header("📖 阶段1: 生成故事内容")
        
        # 检查是否有已保存的会话
        session = self.interactive.load_session(self.story_dir)
        if session and 'pages' in session:
            if self.interactive.get_yes_no("检测到之前的会话，是否继续？", True):
                self.interactive.print_success(f"已加载 {len(session['pages'])} 页故事")
                return session['pages']
        
        # 显示配置信息
        story_config = self.config.get('story_writer', {})
        params = story_config.get('params', {})
        input_type = params.get('input_type', 'topic')
        
        print(f"\n当前配置:")
        print(f"  模式: {input_type}")
        if input_type == 'long_text':
            long_text = params.get('long_text', '')
            print(f"  文本预览: {long_text[:100]}...")
        else:
            print(f"  主题: {params.get('story_topic', 'N/A')}")
        
        target_pages = params.get('target_pages', 8)
        print(f"  目标页数: {target_pages}")
        
        # 确认是否开始生成
        if not self.interactive.get_yes_no("\n开始生成故事？", True):
            return []
        
        # 调用故事生成
        self.interactive.print_info("正在生成故事...")
        
        # 这里使用原有的story_writer
        from mm_story_agent.base import init_tool_instance
        story_writer = init_tool_instance(story_config)
        pages = story_writer.call(params)
        
        self.interactive.print_success(f"故事生成完成！共 {len(pages)} 页")
        
        # 保存中间结果
        self.save_progress({'pages': pages})
        
        return pages
    
    def stage_2_edit_story(self, pages: list) -> list:
        """阶段2: 编辑故事内容"""
        self.interactive.print_header("✏️  阶段2: 编辑故事内容")
        
        # 使用交互式编辑
        edited_pages = self.interactive.review_and_edit_pages(pages)
        
        # 保存编辑结果
        self.save_progress({'pages': edited_pages})
        
        return edited_pages
    
    def stage_3_generate_prompts(self, pages: list) -> list:
        """阶段3: 生成图像提示词"""
        self.interactive.print_header("🎨 阶段3: 生成图像提示词")
        
        self.interactive.print_info("正在为每个页面生成图像提示词...")
        
        # 使用image_agent的提示词生成功能
        from mm_story_agent.base import init_tool_instance
        image_config = self.config.get('image_generation', {})
        
        # 创建临时的image agent来生成提示词
        image_agent = init_tool_instance(image_config)
        
        # 提取角色
        role_dict = image_agent.extract_role_from_story(pages)
        self.interactive.print_success(f"识别到 {len(role_dict)} 个角色")
        
        # 生成提示词
        prompts = image_agent.generate_image_prompt_from_story(pages)
        
        # 添加角色描述
        prompts_with_roles = []
        for prompt in prompts:
            for role, role_desc in role_dict.items():
                if role in prompt:
                    prompt = prompt.replace(role, role_desc)
            prompts_with_roles.append(prompt)
        
        self.interactive.print_success(f"生成了 {len(prompts_with_roles)} 个提示词")
        
        # 保存
        self.save_progress({
            'pages': pages,
            'prompts': prompts_with_roles,
            'roles': role_dict
        })
        
        return prompts_with_roles
    
    def stage_4_edit_prompts(self, prompts: list, pages: list) -> list:
        """阶段4: 优化提示词"""
        self.interactive.print_header("✨ 阶段4: 优化提示词")
        
        # 交互式编辑提示词
        edited_prompts = self.interactive.review_and_edit_prompts(prompts, pages)
        
        # 保存
        self.save_progress({
            'pages': pages,
            'prompts': edited_prompts
        })
        
        return edited_prompts

    def stage_3_5_generate_narration_text(self, pages: list) -> list:
        """
        阶段3.5: 生成讲述性文本（用于语音和字幕）
        将原始故事文本转换为更优美、更有讲述性的文本
        """
        self.interactive.print_header("📝 阶段3.5: 生成讲述性文本")
        
        # 检查是否已有讲述性文本
        session = self.interactive.load_session(self.story_dir)
        if session and 'narration_texts' in session:
            existing_narrations = session.get('narration_texts', [])
            if existing_narrations and len(existing_narrations) >= len(pages):
                if self.interactive.get_yes_no(
                    f"发现已有讲述性文本: {len(existing_narrations)} 个，是否使用？", 
                    True
                ):
                    self.interactive.print_success(f"使用已有讲述性文本: {len(existing_narrations)} 个")
                    return existing_narrations[:len(pages)]
        
        # 询问是否生成讲述性文本
        if not self.interactive.get_yes_no("开始生成讲述性文本？（用于语音和字幕）", True):
            self.interactive.print_warning("跳过讲述性文本生成，将使用原始故事文本")
            return pages
        
        # 获取 LLM 配置
        story_config = self.config.get('story_writer', {})
        cfg = story_config.get('cfg', {})
        api_key = cfg.get('api_key') or os.getenv('DASHSCOPE_API_KEY')
        
        if not api_key:
            self.interactive.print_warning("未找到 DashScope API Key，将使用原始故事文本")
            return pages
        
        # 创建 LLM 代理
        from mm_story_agent.base import init_tool_instance
        llm_config = {
            "tool": "qwen",
            "cfg": {
                "api_key": api_key,
                "system_prompt": "你是一位专业的配音员和字幕撰写专家。你的任务是将故事文本转换为优美、流畅、适合语音播报和字幕显示的讲述性文本。",
                "track_history": False
            }
        }
        
        try:
            llm_agent = init_tool_instance(llm_config)
            self.interactive.print_success("LLM 代理初始化成功")
        except Exception as e:
            self.interactive.print_error(f"初始化 LLM 代理失败: {e}")
            return pages
        
        # 生成讲述性文本
        narration_texts = []
        self.interactive.print_info(f"正在为 {len(pages)} 个页面生成讲述性文本...")
        
        for idx, page_text in enumerate(pages, 1):
            self.interactive.print_info(f"处理第 {idx}/{len(pages)} 页...")
            
            prompt = f"""请将以下故事文本转换为优美、流畅、适合语音播报和字幕显示的讲述性文本。

要求：
1. 保持原意不变，但语言更加优美、生动
2. 适合语音播报，语调自然流畅
3. 适合字幕显示，长度适中（每段不超过50字）
4. 增强讲述性和感染力，让听众更容易理解和感受
5. 不要添加任何额外的说明或格式标记

原始文本：
{page_text}

请直接输出转换后的讲述性文本，不要添加任何前缀或说明："""
            
            try:
                result, success = llm_agent.call(
                    prompt=prompt,
                    model_name="qwen2-72b-instruct",
                    temperature=0.7,
                    max_length=512
                )
                
                if success and result and isinstance(result, str) and result.strip():
                    narration_text = result.strip()
                    narration_texts.append(narration_text)
                    self.interactive.print_success(f"  ✅ 第 {idx} 页生成成功")
                else:
                    self.interactive.print_warning(f"  ⚠️  第 {idx} 页生成结果为空或失败，使用原始文本")
                    narration_texts.append(page_text)
                    
            except Exception as e:
                self.interactive.print_error(f"  ❌ 第 {idx} 页生成失败: {e}，使用原始文本")
                narration_texts.append(page_text)
        
        # 保存结果
        self.save_progress({
            'pages': pages,
            'narration_texts': narration_texts
        })
        
        self.interactive.print_success(f"讲述性文本生成完成！共 {len(narration_texts)} 个")
        
        # 允许用户编辑
        if self.interactive.get_yes_no("是否查看并编辑讲述性文本？", False):
            narration_texts = self.interactive.review_and_edit_pages(narration_texts)
            self.save_progress({
                'pages': pages,
                'narration_texts': narration_texts
            })
        
        return narration_texts

    def stage_5_generate_images(self, prompts: list, pages: list) -> list:
        """阶段5: 生成图像（优化版 - 并行生成）"""
        self.interactive.print_header("🖼️ 阶段5: 生成图像（并行模式）")
        
        self.interactive.print_info(f"准备并行生成 {len(prompts)} 张图片...")
        
        if not self.interactive.get_yes_no("开始生成图像？（使用并行加速）", True):
            self.interactive.print_warning("跳过图像生成")
            return []
        
        # 获取配置
        image_config = self.config.get('image_generation', {})
        params = image_config.get('params', {})
        
        # 优先从环境变量读取 API key
        import os
        api_key = params.get('api_key') or os.getenv('DASHSCOPE_API_KEY')
        
        if not api_key:
            self.interactive.print_error("未配置 api_key")
            self.interactive.print_warning("请在配置文件中设置 api_key 或运行 setup_env.ps1")
            return []
        
        # 准备路径
        image_dir = self.story_dir / "image"
        cache_dir = self.story_dir / ".cache"
        
        # 优化提示词
        from mm_story_agent.base import init_tool_instance
        image_agent = init_tool_instance(image_config)
        style_name = params.get('style_name', 'Storybook')
        
        optimized_prompts = []
        for prompt in prompts:
            opt_prompt = image_agent.optimize_prompt(prompt, style_name)
            optimized_prompts.append(opt_prompt)
        
        # 创建优化生成器
        generator = OptimizedImageGenerator(
            api_key=api_key,
            max_concurrent=params.get('max_concurrent', 4),
            request_interval=params.get('request_interval', 1.0),
            cache_dir=cache_dir if params.get('enable_cache', True) else None,
            max_retries=params.get('max_retries', 3)
        )
        try:
            # 检查是否有运行中的事件循环
            loop = asyncio.get_event_loop()
            if loop.is_running():
                # 如果有运行中的循环，创建新的循环
                import nest_asyncio
                nest_asyncio.apply()
        except RuntimeError:
            pass
        # 并行生成
        self.interactive.print_info("⚡ 使用并行模式生成中...")
        images = asyncio.run(
            generator.generate_batch(optimized_prompts, image_dir)
        )
        
        # 保存进度
        self.save_progress({
            'pages': pages,
            'prompts': prompts,
            'images': images
        })
        
        # 统计成功率（排除占位符路径和None）
        def is_valid_image(img):
            if not img:
                return False
            if isinstance(img, str) and img.endswith("_error.png"):
                return False
            return Path(img).exists()
        
        success_count = sum(1 for img in images if is_valid_image(img))
        failed_count = len(images) - success_count
        self.interactive.print_success(
            f"图像生成完成！成功 {success_count}/{len(images)} 张"
        )
        if failed_count > 0:
            self.interactive.print_warning(f"失败 {failed_count} 张，可在阶段6重新生成")
        
        return images
    
    def stage_6_regenerate_images(self, image_paths: list, prompts: list, pages: list) -> list:
        """阶段6: 检查并重新生成图像"""
        self.interactive.print_header("🔄 阶段6: 图像检查与重新生成")

        # 🔥 添加检查：如果没有图像或全部失败，直接返回
        def is_valid_image(img):
            if not img:
                return False
            if isinstance(img, str) and img.endswith("_error.png"):
                return False
            return Path(img).exists()
        
        valid_images = [img for img in image_paths if is_valid_image(img)]
        if not image_paths or not valid_images:
            self.interactive.print_error("没有成功生成的图像，无法进入重新生成阶段")
            self.interactive.print_warning("请检查:")
            self.interactive.print_warning("  1. API key 是否正确配置")
            self.interactive.print_warning("  2. 网络连接是否正常")
            self.interactive.print_warning("  3. API 额度是否充足")
            
            if self.interactive.get_yes_no("是否重新尝试生成所有图像？", True):
                return self.stage_5_generate_images(prompts, pages)
            else:
                return image_paths
        
        # 让用户选择需要重新生成的图像
        regenerate_indices = self.interactive.review_images_and_select_regenerate(
            image_paths, pages
        )
        
        if not regenerate_indices:
            self.interactive.print_success("无需重新生成图像")
            return image_paths
        
        # 重新生成选定的图像
        self.interactive.print_info(f"开始重新生成 {len(regenerate_indices)} 张图片...")
        
        from mm_story_agent.base import init_tool_instance
        image_config = self.config.get('image_generation', {})
        image_agent = init_tool_instance(image_config)
        
        image_dir = self.story_dir / "image"
        style_name = image_config.get('params', {}).get('style_name', 'Storybook')
        
        new_image_paths = image_paths.copy()
        
        for idx in regenerate_indices:
            try:
                self.interactive.print_info(f"重新生成第 {idx + 1} 张图片...")
                
                # 询问是否修改提示词
                if self.interactive.get_yes_no(f"是否修改第 {idx + 1} 张图片的提示词？", False):
                    print(f"原提示词: {prompts[idx]}")
                    new_prompt = self.interactive.get_input("新提示词", prompts[idx])
                    prompts[idx] = new_prompt
                
                # 重新生成
                image_path = image_agent.call_dashscope_api_with_wait(
                    prompt=image_agent.optimize_prompt(prompts[idx], style_name),
                    save_path=image_dir,
                    index=idx + 1
                )
                new_image_paths[idx] = image_path
                self.interactive.print_success(f"✅ 第 {idx + 1} 张图片重新生成成功")
                
                # 等待
                import time
                time.sleep(image_agent.request_interval)
                
            except Exception as e:
                self.interactive.print_error(f"第 {idx + 1} 张图片重新生成失败: {e}")
        
        # 保存
        self.save_progress({
            'pages': pages,
            'prompts': prompts,
            'images': new_image_paths
        })
        
        return new_image_paths
    
    def stage_5_6_generate_videos(self, prompts: list, pages: list, image_paths: list = None) -> tuple:
        """
        阶段5.6: 使用Runway API通过提示词生成视频片段
        如果视频生成失败，回退到文生图
        
        Returns:
            (video_paths, image_paths): 视频路径列表和图片路径列表（用于回退）
        """
        self.interactive.print_header("🎬 阶段5.6: 生成视频片段（Runway API）")
        
        # 检查Runway配置
        runway_config = self.config.get('runway', {})
        if not runway_config.get('enabled', False):
            self.interactive.print_error("Runway API未启用，无法生成视频片段")
            self.interactive.print_warning("提示：请在配置文件中启用 runway.enabled = true")
            return ([], image_paths or [])
        
        # 获取API密钥
        import os
        runway_api_key = runway_config.get('api_key') or os.getenv('RUNWAY_API_KEY')
        if not runway_api_key:
            self.interactive.print_error("未配置Runway API密钥，无法生成视频片段")
            self.interactive.print_warning("提示：请在配置文件中设置 runway.api_key 或设置环境变量 RUNWAY_API_KEY")
            return ([], image_paths or [])
        
        # 准备视频保存目录
        video_dir = self.story_dir / "videos"
        video_dir.mkdir(parents=True, exist_ok=True)
        
        # 导入Runway视频生成函数
        from mm_story_agent.video_compose_agent import generate_video_from_text_with_runway
        
        # 获取Runway配置参数
        model = runway_config.get('model', 'gen4')
        ratio = runway_config.get('ratio', '16:9')
        timeout = runway_config.get('timeout', 300)
        
        video_paths = []
        fallback_image_paths = []
        image_dir = self.story_dir / "image"
        image_dir.mkdir(parents=True, exist_ok=True)
        
        self.interactive.print_info(f"开始为 {len(prompts)} 个提示词生成视频片段...")
        self.interactive.print_info("提示：如果视频生成失败，将自动调用文生图作为替代")
        
        # 为每个提示词生成视频
        for idx, prompt in enumerate(prompts):
            page_num = idx + 1
            self.interactive.print_info(f"\n处理第 {page_num}/{len(prompts)} 个片段...")
            self.interactive.print_info(f"提示词: {prompt[:60]}...")
            
            # 步骤1: 尝试生成视频
            video_file = None
            try:
                self.interactive.print_info(f"  🎬 尝试生成视频片段...")
                video_file = generate_video_from_text_with_runway(
                    prompt_text=prompt,
                    api_key=runway_api_key,
                    duration=5,  # 默认5秒
                    model=model,
                    ratio=ratio,
                    timeout=timeout,
                    save_dir=video_dir,
                    filename=f"p{page_num:04d}.mp4"
                )
                
                if video_file and Path(video_file).exists():
                    video_paths.append(video_file)
                    self.interactive.print_success(f"  ✅ 第 {page_num} 个视频片段生成成功")
                else:
                    # 视频生成失败，回退到文生图
                    self.interactive.print_warning(f"  ⚠️  视频生成失败，调用文生图作为替代...")
                    video_file = None
                    
            except Exception as e:
                self.interactive.print_error(f"  ❌ 生成视频时出错: {e}")
                video_file = None
            
            # 步骤2: 如果视频生成失败，调用 DashScope 文生图（不是 Runway）
            if video_file is None:
                fallback_image = self._generate_fallback_image(
                    prompt, page_num, image_dir
                )
                if fallback_image:
                    fallback_image_paths.append(fallback_image)
                    video_paths.append(None)  # 标记为图片
                    self.interactive.print_success(f"  ✅ 第 {page_num} 个片段使用图片替代")
                else:
                    # 如果图片生成也失败
                    fallback_image_paths.append(None)
                    video_paths.append(None)
                    self.interactive.print_error(f"  ❌ 第 {page_num} 个片段生成失败（视频和图片都失败）")
        
        # 统计结果
        success_videos = sum(1 for v in video_paths if v is not None)
        fallback_images = sum(1 for v in video_paths if v is None)
        
        self.interactive.print_success(
            f"\n视频生成完成！成功: {success_videos} 个视频，回退到图片: {fallback_images} 个"
        )
        
        # 保存进度
        self.save_progress({
            'pages': pages,
            'prompts': prompts,
            'video_paths': video_paths,
            'image_paths': fallback_image_paths
        })
        
        return (video_paths, fallback_image_paths)
    
    def _generate_fallback_image(self, prompt: str, page_num: int, 
                                 image_dir: Path) -> Optional[str]:
        """
        生成回退图片（当视频生成失败时）
        使用 DashScope 的图片生成服务（与 stage_5_generate_images 相同）
        """
        try:
            # 使用与 stage_5_generate_images 相同的图片生成方式
            image_config = self.config.get('image_generation', {})
            params = image_config.get('params', {})
            
            # 优先从环境变量读取 API key
            import os
            api_key = params.get('api_key') or os.getenv('DASHSCOPE_API_KEY')
            
            if not api_key:
                self.interactive.print_error("未配置 DashScope API key，无法生成回退图片")
                return None
            
            # 创建图片生成代理
            from mm_story_agent.base import init_tool_instance
            image_agent = init_tool_instance(image_config)
            style_name = params.get('style_name', 'Storybook')
            
            # 优化提示词
            optimized_prompt = image_agent.optimize_prompt(prompt, style_name)
            
            # 调用 DashScope API 生成图片
            self.interactive.print_info(f"  🖼️  使用 DashScope API 生成图片...")
            image_file = image_agent.call_dashscope_api_with_wait(
                prompt=optimized_prompt,
                save_path=image_dir,
                index=page_num
            )
            
            if image_file and Path(image_file).exists():
                self.interactive.print_success(f"✅ 回退图片生成成功: {Path(image_file).name}")
                return image_file
            else:
                return None
                
        except Exception as e:
            self.interactive.print_error(f"回退图片生成失败: {e}")
            import traceback
            traceback.print_exc()
            return None
    
    def _check_existing_assets(self, pages: list = None) -> tuple:
        """
        检查目标目录中是否已有提示词和音频文件
        
        Args:
            pages: 页面列表（可选，用于验证数量是否匹配）
        
        Returns:
            (existing_prompts, existing_speech_files): 
            - existing_prompts: 已有的提示词列表（如果存在）
            - existing_speech_files: 已有的音频文件列表（如果存在）
        """
        existing_prompts = None
        existing_speech_files = []
        
        # 检查会话文件中是否有提示词
        session = self.interactive.load_session(self.story_dir)
        if session:
            # 检查是否有提示词
            if 'prompts' in session and session['prompts']:
                prompts = session['prompts']
                if isinstance(prompts, list) and len(prompts) > 0:
                    existing_prompts = prompts
        
        # 检查语音目录中是否有音频文件
        speech_dir = self.story_dir / "speech"
        if speech_dir.exists():
            # 查找所有音频文件（支持多种命名格式）
            audio_files = []
            # 查找所有 p*.wav 文件（排除分块文件 p*_*.wav）
            all_wav_files = list(speech_dir.glob("p*.wav"))
            for f in all_wav_files:
                # 排除分块文件（如 p1_001.wav, p2_002.wav）
                # 分块文件的特征是：p数字_数字.wav
                import re
                if not re.match(r'p\d+_\d+\.wav$', f.name):
                    audio_files.append(f)
            
            # 去重并排序（按文件名中的数字排序）
            def extract_number(filename):
                import re
                # 提取文件名中的数字（p1.wav -> 1, p0001.wav -> 1）
                match = re.search(r'p(\d+)', filename.name)
                return int(match.group(1)) if match else 0
            
            audio_files = sorted(set(audio_files), key=extract_number)
            
            if audio_files:
                existing_speech_files = [str(f) for f in audio_files]
        
        return (existing_prompts, existing_speech_files)
    
    def save_progress(self, data: dict):
        """保存进度"""
        self.interactive.save_session(data, self.story_dir)

        # 阶段9: 生成交互式 HTML 版本
    # def stage_9_generate_html(self, pages: list, visualizations: list):
    #     from mm_story_agent.modality_agents.interactive_narrative_agent import InteractiveNarrativeAgent
        
    #     html_agent = InteractiveNarrativeAgent(
    #         output_dir=self.story_dir,
    #         theme="fantasy"
    #     )
        
    #     html_path = html_agent.generate_interactive_story(
    #         pages=pages,
    #         title="我的互动故事",
    #         visualizations=visualizations
    #     )
        
    #     print(f"✅ HTML 版本已生成: {html_path}")

    def stage_9_generate_interactive_html(self, pages: list, viz_paths: list = None):
        """阶段9: 生成交互式HTML"""
        self.interactive.print_header("🌐 阶段9: 生成交互式HTML")
        
        # 询问用户
        if not self.interactive.get_yes_no("是否生成交互式HTML版本？", True):
            self.interactive.print_warning("跳过HTML生成")
            return
        
        # 导入交互式叙事代理
        from mm_story_agent.modality_agents.interactive_narrative_agent import InteractiveNarrativeAgent
        
        # 获取故事标题
        story_config = self.config.get('story_writer', {})
        params = story_config.get('params', {})
        title = params.get('story_topic', '我的故事')
        
        # 选择主题
        theme_choice = self.interactive.get_choice(
            "选择HTML主题",
            ["默认", "暗黑", "奇幻", "科幻"],
            0
        )
        theme_map = {0: 'default', 1: 'dark', 2: 'fantasy', 3: 'scifi'}
        theme = theme_map[theme_choice]
        
        # 创建代理
        html_agent = InteractiveNarrativeAgent(
            output_dir=self.story_dir.parent / "interactive_stories",
            theme=theme
        )
        
        # 准备可视化数据
        visualizations = None
        if viz_paths:
            # 从可视化路径提取数据
            from mm_story_agent.modality_agents.data_visualization_agent import DataVisualizationAgent
            viz_agent = DataVisualizationAgent(
                output_dir=self.story_dir / "visualizations",
                style='colorful'
            )
            visualizations = viz_agent.extract_data_from_story(pages)
            if not visualizations:
                visualizations = viz_agent._create_demo_visualizations(len(pages))
        
        # 生成HTML
        self.interactive.print_info("正在生成交互式HTML...")
        html_path = html_agent.generate_interactive_story(
            pages=pages,
            title=title,
            visualizations=visualizations
        )
        
        self.interactive.print_success(f"✅ HTML已生成: {html_path}")
        
        # 询问是否在浏览器中打开
        if self.interactive.get_yes_no("是否在浏览器中打开？", True):
            import webbrowser
            webbrowser.open(f"file://{Path(html_path).absolute()}")
            
    def enhanced_stage_1(self):
        """增强的阶段1"""
        # 选择生成模式
        mode = self.interactive.get_choice(
            "选择生成模式",
            ["一次性生成", "逐段生成", "取消"],
            1
        )
        
        if mode == 1:
            return self.incremental_generation()
        else:
            return self.stage_1_generate_story()
        
        # def incremental_generation(self) -> list:
        #     """逐段生成故事"""
        #     self.interactive.print_section("逐段生成模式")
            
        #     story_config = self.config.get('story_writer', {})
        #     params = story_config.get('params', {})
        #     target_pages = int(self.interactive.get_input("目标页数", str(params.get('target_pages', 8))))
            
        #     pages = []
            
        #     for i in range(target_pages):
        #         self.interactive.print_info(f"\n正在生成第 {i+1}/{target_pages} 页...")
                
        #         # 调用原有的生成功能（这里简化）
        #         # 实际使用时需要修改 story_writer 支持单页生成
        #         from mm_story_agent.base import init_tool_instance
        #         story_writer = init_tool_instance(story_config)
                
        #         # 临时方案：生成完整故事后取当前页
        #         # 你需要根据实际情况修改这部分
        #         if not pages:
        #             all_pages = story_writer.call(params)
        #             if i < len(all_pages):
        #                 new_page = all_pages[i]
        #             else:
        #                 new_page = f"第 {i+1} 页内容"
        #         else:
        #             new_page = f"基于前文生成的第 {i+1} 页"
                
        #         # 显示生成结果
        #         # print(f"\n{self.interactive.Colors.CYAN}生成内容:{self.interactive.Colors.END}")
        #         # print(new_page)
        #         self.interactive.print_info("生成内容:")
        #         print(new_page)
                
        #         # 用户选择
        #         action = self.interactive.get_choice(
        #             "\n对这一页满意吗？",
        #             ["✅ 满意，继续", "🔄 重新生成", "✏️  手动编辑", "❌ 停止生成"],
        #             0
        #         )
                
        #         if action == 0:
        #             pages.append(new_page)
        #         elif action == 1:
        #             # 重新生成（简化：直接添加）
        #             pages.append(new_page)
        #         elif action == 2:
        #             # edited = input(f"{self.interactive.Colors.CYAN}请输入修改后的内容: {self.interactive.Colors.END}").strip()
        #             edited = self.interactive.get_input("请输入修改后的内容", new_page).strip()
        #             pages.append(edited if edited else new_page)
        #         else:
        #             break
            
        #     self.save_progress({'pages': pages})
        #     return pages
    def incremental_generation(self) -> list:
        """逐段生成故事"""
        self.interactive.print_section("逐段生成模式")
        
        story_config = self.config.get('story_writer', {})
        params = story_config.get('params', {})
        target_pages = int(self.interactive.get_input("目标页数", str(params.get('target_pages', 8))))
        
        # 先生成完整故事
        from mm_story_agent.base import init_tool_instance
        story_writer = init_tool_instance(story_config)
        
        self.interactive.print_info("正在生成完整故事...")
        all_pages = story_writer.call(params)
        
        if len(all_pages) < target_pages:
            self.interactive.print_warning(f"只生成了 {len(all_pages)} 页，少于目标 {target_pages} 页")
            target_pages = len(all_pages)
        
        pages = []
        
        # 逐页展示并让用户确认/编辑
        for i in range(target_pages):
            self.interactive.print_info(f"\n正在展示第 {i+1}/{target_pages} 页...")
            
            new_page = all_pages[i] if i < len(all_pages) else f"第 {i+1} 页内容"
            
            # 显示生成结果
            self.interactive.print_info("生成内容:")
            print(new_page)
            
            # 用户选择
            action = self.interactive.get_choice(
                "\n对这一页满意吗？",
                ["✅ 满意,继续", "🔄 重新生成此页", "✏️ 手动编辑", "❌ 停止生成"],
                0
            )
            
            if action == 0:  # 满意
                pages.append(new_page)
            elif action == 1:  # 重新生成
                self.interactive.print_info("重新生成当前页...")
                # 重新调用生成（这里简化处理，实际可以调用单页生成）
                regenerated = story_writer.call(params)
                if i < len(regenerated):
                    pages.append(regenerated[i])
                else:
                    pages.append(new_page)
            elif action == 2:  # 手动编辑
                edited = self.interactive.get_input("请输入修改后的内容", new_page).strip()
                pages.append(edited if edited else new_page)
            else:  # 停止
                break
        
        self.save_progress({'pages': pages})
        return pages

    def stage_6_regenerate_images(self, image_paths: list, prompts: list, pages: list) -> list:
        """阶段6: 检查并重新生成图像"""
        self.interactive.print_header("🔄 阶段6: 图像检查与重新生成")

        # 🔥 添加检查：如果没有图像，直接返回
        if not image_paths or all(img is None for img in image_paths):
            self.interactive.print_error("没有成功生成的图像，无法进入重新生成阶段")
            self.interactive.print_warning("请检查:")
            self.interactive.print_warning("  1. API key 是否正确配置")
            self.interactive.print_warning("  2. 网络连接是否正常")
            self.interactive.print_warning("  3. API 额度是否充足")
            
            if self.interactive.get_yes_no("是否重新尝试生成所有图像？", True):
                return self.stage_5_generate_images(prompts, pages)
            else:
                return image_paths
        
        # 让用户选择需要重新生成的图像
        regenerate_indices = self.interactive.review_images_and_select_regenerate(
            image_paths, pages
        )
        
        if not regenerate_indices:
            self.interactive.print_success("无需重新生成图像")
            return image_paths
        
        # 重新生成选定的图像
        self.interactive.print_info(f"开始重新生成 {len(regenerate_indices)} 张图片...")
        
        from mm_story_agent.base import init_tool_instance
        image_config = self.config.get('image_generation', {})
        image_agent = init_tool_instance(image_config)
        
        image_dir = self.story_dir / "image"
        style_name = image_config.get('params', {}).get('style_name', 'Storybook')
        
        new_image_paths = image_paths.copy()
        
        for idx in regenerate_indices:
            try:
                self.interactive.print_info(f"重新生成第 {idx + 1} 张图片...")
                
                # 询问是否修改提示词
                if self.interactive.get_yes_no(f"是否修改第 {idx + 1} 张图片的提示词？", False):
                    print(f"原提示词: {prompts[idx]}")
                    new_prompt = self.interactive.get_input("新提示词", prompts[idx])
                    prompts[idx] = new_prompt
                
                # 重新生成
                image_path = image_agent.call_dashscope_api_with_wait(
                    prompt=image_agent.optimize_prompt(prompts[idx], style_name),
                    save_path=image_dir,
                    index=idx + 1
                )
                new_image_paths[idx] = image_path
                self.interactive.print_success(f"✅ 第 {idx + 1} 张图片重新生成成功")
                
                # 等待
                import time
                time.sleep(image_agent.request_interval)
                
            except Exception as e:
                self.interactive.print_error(f"第 {idx + 1} 张图片重新生成失败: {e}")
        
        # 保存
        self.save_progress({
            'pages': pages,
            'prompts': prompts,
            'images': new_image_paths
        })
        
        return new_image_paths

    def stage_7_final_confirmation(self, pages: list, image_paths: list, video_paths: list = None) -> bool:
        """阶段7: 最终确认"""
        self.interactive.print_header("✅ 阶段7: 最终确认")
        
        # 如果有视频片段，显示视频信息
        if video_paths:
            video_count = sum(1 for v in video_paths if v is not None)
            image_count = sum(1 for v in video_paths if v is None)
            if video_count > 0:
                self.interactive.print_info(f"已生成 {video_count} 个视频片段")
            if image_count > 0:
                self.interactive.print_info(f"回退到图片: {image_count} 个")
        
        return self.interactive.final_confirmation(pages, image_paths)
    


    def stage_8_compose_video(self, narration_texts: list, image_paths: list, video_paths: list = None):
        """
        阶段8: 合成视频（支持视频片段和图片混合）
        
        Args:
            narration_texts: 讲述性文本列表（用于字幕）
            image_paths: 图片路径列表（用于回退）
            video_paths: 视频片段路径列表（None表示该页面使用图片）
        """
        self.interactive.print_header("🎬 阶段8: 合成视频")
        
        self.interactive.print_info("正在合成视频...")
        
        # 调用视频合成
        video_config = self.config.get('video_compose', {})
        
        if not video_config:
            self.interactive.print_warning("未配置视频合成，跳过")
            return
        
        from mm_story_agent.base import init_tool_instance
        video_agent = init_tool_instance(video_config)
        
        # 准备参数（使用讲述性文本作为字幕）
        params = video_config.get('params', {}).copy()
        params['pages'] = narration_texts  # 使用讲述性文本作为字幕
        params['story_dir'] = str(self.story_dir)
        
        # 🔥 新增：传入视频片段路径列表
        if video_paths:
            # 转换为字符串路径（如果是 Path 对象）
            video_paths_str = []
            for v in video_paths:
                if v is not None:
                    video_paths_str.append(str(v))
                else:
                    video_paths_str.append(None)
            params['video_paths'] = video_paths_str
            
            video_count = sum(1 for v in video_paths if v is not None)
            image_count = sum(1 for v in video_paths if v is None)
            self.interactive.print_info(f"使用 {video_count} 个视频片段，{image_count} 个图片")
            
            for idx, v in enumerate(video_paths, start=1):
                if v is not None:
                    self.interactive.print_info(f"  {idx}. [视频] {Path(v).name}")
                elif idx <= len(image_paths) and image_paths[idx-1]:
                    self.interactive.print_info(f"  {idx}. [图片] {Path(image_paths[idx-1]).name}")
        
        # 🔥 关键修复：传入图片路径列表（用于回退的图片）
        if image_paths:
            # 转换为字符串路径（如果是 Path 对象）
            image_paths_str = [str(p) if p else None for p in image_paths]
            # 过滤掉 None 值
            image_paths_str = [p for p in image_paths_str if p is not None]
            params['image_paths'] = image_paths_str
            
            if not video_paths or all(v is None for v in video_paths):
                self.interactive.print_info(f"使用 {len(image_paths_str)} 个图片文件")
                for idx, path in enumerate(image_paths_str, start=1):
                    self.interactive.print_info(f"  {idx}. {Path(path).name}")
        else:
            if not video_paths or all(v is None for v in video_paths):
                self.interactive.print_warning("未提供图片路径，将尝试自动查找")
        
        # 🔥 新增：启用Runway支持（如果提供了视频片段）
        if video_paths and any(v is not None for v in video_paths):
            runway_config = self.config.get('runway', {})
            if runway_config.get('enabled', False):
                params['use_runway'] = True
                import os
                params['runway_api_key'] = runway_config.get('api_key') or os.getenv('RUNWAY_API_KEY')
                # 准备Runway配置
                runway_api_config = {
                    "model": runway_config.get('model', 'gen4'),
                    "ratio": runway_config.get('ratio', '16:9'),
                    "timeout": runway_config.get('timeout', 300)
                }
                params['runway_config'] = runway_api_config
        
        # 🔥 新增：传递图片生成配置（用于 DashScope fallback）
        image_generation_config = self.config.get('image_generation', None)
        if image_generation_config:
            params['image_generation'] = image_generation_config
        
        # 执行合成
        video_agent.call(params)
        
        output_path = self.story_dir / "output.mp4"
        self.interactive.print_success(f"视频已保存: {output_path}")
        
    def save_progress(self, data: dict):
        """保存进度"""
        self.interactive.save_session(data, self.story_dir)

        # 阶段9: 生成交互式 HTML 版本
    # def stage_9_generate_html(self, pages: list, visualizations: list):
    #     from mm_story_agent.modality_agents.interactive_narrative_agent import InteractiveNarrativeAgent
        
    #     html_agent = InteractiveNarrativeAgent(
    #         output_dir=self.story_dir,
    #         theme="fantasy"
    #     )
        
    #     html_path = html_agent.generate_interactive_story(
    #         pages=pages,
    #         title="我的互动故事",
    #         visualizations=visualizations
    #     )
        
    #     print(f"✅ HTML 版本已生成: {html_path}")

    def stage_9_generate_interactive_html(self, pages: list, viz_paths: list = None):
        """阶段9: 生成交互式HTML"""
        self.interactive.print_header("🌐 阶段9: 生成交互式HTML")
        
        # 询问用户
        if not self.interactive.get_yes_no("是否生成交互式HTML版本？", True):
            self.interactive.print_warning("跳过HTML生成")
            return
        
        # 导入交互式叙事代理
        from mm_story_agent.modality_agents.interactive_narrative_agent import InteractiveNarrativeAgent
        
        # 获取故事标题
        story_config = self.config.get('story_writer', {})
        params = story_config.get('params', {})
        title = params.get('story_topic', '我的故事')
        
        # 选择主题
        theme_choice = self.interactive.get_choice(
            "选择HTML主题",
            ["默认", "暗黑", "奇幻", "科幻"],
            0
        )
        theme_map = {0: 'default', 1: 'dark', 2: 'fantasy', 3: 'scifi'}
        theme = theme_map[theme_choice]
        
        # 创建代理
        html_agent = InteractiveNarrativeAgent(
            output_dir=self.story_dir.parent / "interactive_stories",
            theme=theme
        )
        
        # 准备可视化数据
        visualizations = None
        if viz_paths:
            # 从可视化路径提取数据
            from mm_story_agent.modality_agents.data_visualization_agent import DataVisualizationAgent
            viz_agent = DataVisualizationAgent(
                output_dir=self.story_dir / "visualizations",
                style='colorful'
            )
            visualizations = viz_agent.extract_data_from_story(pages)
            if not visualizations:
                visualizations = viz_agent._create_demo_visualizations(len(pages))
        
        # 生成HTML
        self.interactive.print_info("正在生成交互式HTML...")
        html_path = html_agent.generate_interactive_story(
            pages=pages,
            title=title,
            visualizations=visualizations
        )
        
        self.interactive.print_success(f"✅ HTML已生成: {html_path}")
        
        # 询问是否在浏览器中打开
        if self.interactive.get_yes_no("是否在浏览器中打开？", True):
            import webbrowser
            webbrowser.open(f"file://{Path(html_path).absolute()}")
            
        def enhanced_stage_1(self):
            """增强的阶段1"""
            # 选择生成模式
            mode = self.interactive.get_choice(
                "选择生成模式",
                ["一次性生成", "逐段生成", "取消"],
                1
            )
            
            if mode == 1:
                return self.incremental_generation()
            else:
                return self.stage_1_generate_story()
            
            # def incremental_generation(self) -> list:
            #     """逐段生成故事"""
            #     self.interactive.print_section("逐段生成模式")
                
            #     story_config = self.config.get('story_writer', {})
            #     params = story_config.get('params', {})
            #     target_pages = int(self.interactive.get_input("目标页数", str(params.get('target_pages', 8))))
                
            #     pages = []
                
            #     for i in range(target_pages):
            #         self.interactive.print_info(f"\n正在生成第 {i+1}/{target_pages} 页...")
                    
            #         # 调用原有的生成功能（这里简化）
            #         # 实际使用时需要修改 story_writer 支持单页生成
            #         from mm_story_agent.base import init_tool_instance
            #         story_writer = init_tool_instance(story_config)
                    
            #         # 临时方案：生成完整故事后取当前页
            #         # 你需要根据实际情况修改这部分
            #         if not pages:
            #             all_pages = story_writer.call(params)
            #             if i < len(all_pages):
            #                 new_page = all_pages[i]
            #             else:
            #                 new_page = f"第 {i+1} 页内容"
            #         else:
            #             new_page = f"基于前文生成的第 {i+1} 页"
                    
            #         # 显示生成结果
            #         # print(f"\n{self.interactive.Colors.CYAN}生成内容:{self.interactive.Colors.END}")
            #         # print(new_page)
            #         self.interactive.print_info("生成内容:")
            #         print(new_page)
                    
            #         # 用户选择
            #         action = self.interactive.get_choice(
            #             "\n对这一页满意吗？",
            #             ["✅ 满意，继续", "🔄 重新生成", "✏️  手动编辑", "❌ 停止生成"],
            #             0
            #         )
                    
            #         if action == 0:
            #             pages.append(new_page)
            #         elif action == 1:
            #             # 重新生成（简化：直接添加）
            #             pages.append(new_page)
            #         elif action == 2:
            #             # edited = input(f"{self.interactive.Colors.CYAN}请输入修改后的内容: {self.interactive.Colors.END}").strip()
            #             edited = self.interactive.get_input("请输入修改后的内容", new_page).strip()
            #             pages.append(edited if edited else new_page)
            #         else:
            #             break
                
            #     self.save_progress({'pages': pages})
            #     return pages
    def incremental_generation(self) -> list:
        """逐段生成故事"""
        self.interactive.print_section("逐段生成模式")
        
        story_config = self.config.get('story_writer', {})
        params = story_config.get('params', {})
        target_pages = int(self.interactive.get_input("目标页数", str(params.get('target_pages', 8))))
        
        # 先生成完整故事
        from mm_story_agent.base import init_tool_instance
        story_writer = init_tool_instance(story_config)
        
        self.interactive.print_info("正在生成完整故事...")
        all_pages = story_writer.call(params)
        
        if len(all_pages) < target_pages:
            self.interactive.print_warning(f"只生成了 {len(all_pages)} 页，少于目标 {target_pages} 页")
            target_pages = len(all_pages)
        
        pages = []
        
        # 逐页展示并让用户确认/编辑
        for i in range(target_pages):
            self.interactive.print_info(f"\n正在展示第 {i+1}/{target_pages} 页...")
            
            new_page = all_pages[i] if i < len(all_pages) else f"第 {i+1} 页内容"
            
            # 显示生成结果
            self.interactive.print_info("生成内容:")
            print(new_page)
            
            # 用户选择
            action = self.interactive.get_choice(
                "\n对这一页满意吗？",
                ["✅ 满意,继续", "🔄 重新生成此页", "✏️ 手动编辑", "❌ 停止生成"],
                0
            )
            
            if action == 0:  # 满意
                pages.append(new_page)
            elif action == 1:  # 重新生成
                self.interactive.print_info("重新生成当前页...")
                # 重新调用生成（这里简化处理，实际可以调用单页生成）
                regenerated = story_writer.call(params)
                if i < len(regenerated):
                    pages.append(regenerated[i])
                else:
                    pages.append(new_page)
            elif action == 2:  # 手动编辑
                edited = self.interactive.get_input("请输入修改后的内容", new_page).strip()
                pages.append(edited if edited else new_page)
            else:  # 停止
                break
        
        self.save_progress({'pages': pages})
        return pages

def main():
    parser = argparse.ArgumentParser(
        description='交互式视频生成系统 - 支持对话式生成和内容优化',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  # 使用GUI选择文件
  python run_interactive.py --config configs/novel_example.yaml
  
  # 命令行指定文件
  python run_interactive.py -c configs/novel_example.yaml --file "D:/novel.pdf"
  
  # 批量处理目录
  python run_interactive.py -c configs/novel_example.yaml --dir "D:/novels/"
  
  # 使用环境变量
  set INPUT_FILE=D:/novel.pdf
  python run_interactive.py --config configs/novel_example.yaml
        """
    )
    
    parser.add_argument("--config", "-c", type=str, required=True,
                       help="配置文件路径")
    
    # 🔥 新增: 文件/目录选择参数
    parser.add_argument("--file", "-f", type=str,
                       help="指定要处理的文件路径(覆盖配置文件)")
    
    parser.add_argument("--dir", "-d", type=str,
                       help="指定要处理的目录路径(批量处理)")
    
    parser.add_argument("--no-gui", action="store_true",
                       help="禁用GUI文件选择器(用于非交互式环境)")
    
    parser.add_argument("--auto", action="store_true",
                       help="自动模式(跳过所有交互)")
    
    args = parser.parse_args()
    
    # 加载配置
    config_path = Path(args.config)
    if not config_path.exists():
        print(f"❌ 配置文件不存在: {args.config}")
        sys.exit(1)
    
    with open(config_path, 'r', encoding='utf-8') as f:
         config = yaml.load(f, Loader=yaml.FullLoader)
    
    # 🔥 解析输入路径
    if not resolve_input_path(config, args):
        print("\n❌ 无法解析输入路径,程序退出")
        sys.exit(1)
    
    # 显示最终配置摘要
    print("\n" + "="*60)
    print("📊 最终配置摘要")
    print("="*60)
    params = config['story_writer']['params']
    
    if params.get('file_path'):
        print(f"  输入文件: {params['file_path']}")
    elif params.get('dir_path'):
        print(f"  输入目录: {params['dir_path']}")
    elif params.get('long_text'):
        print(f"  直接文本输入: {len(params['long_text'])} 字符")
    
    print(f"  目标页数: {params.get('target_pages', 8)}")
    print(f"  输出目录: {config.get('story_dir')}")
    print("="*60)

    # 运行交互式生成器
    generator = InteractiveVideoGenerator(config)
    generator.run()

if __name__ == "__main__":
    main()